package com.cmpe451.nutty.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

/**
 * @author CmpE Group 2 Fall 2014 nuttyconfirmation@gmail.com
 * 
 */
public class CustomDateUtils {

	public static final long ONE_DAY = 24 * 60 * 60 * 1000;

	public static SimpleDateFormat LOG_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm z");

	public static Date currentDate() {
		return new Date();
	}

	public static Date convertToGivenTimezone(Date date, Double timezoneHourOffset) {
		DateTime dt = new DateTime(date.getTime());

		Double currentTimezoneHourOffset = findTimeZoneHourOffset(date);
		if (currentTimezoneHourOffset != 0) {
			dt = dt.minusMinutes((int) (currentTimezoneHourOffset * 60));
		}

		if (timezoneHourOffset != null) {
			dt = dt.plusMinutes((int) (timezoneHourOffset * 60));
		}

		return dt.toDate();
	}

	public static Date convertLocalToUTC(Date date) {
		DateTimeZone zone = DateTimeZone.getDefault();
		long utc = zone.convertLocalToUTC(date.getTime(), false);
		return new Date(utc);
	}

	public static Date getBeginDate(Date date) {
		Calendar cal = new GregorianCalendar();
		cal.setTime(date);
		getBeginDate(cal);
		return cal.getTime();
	}

	public static Date getBeginHour(Date date) {
		Calendar cal = new GregorianCalendar();
		cal.setTime(date);
		getBeginHour(cal);
		return cal.getTime();
	}

	public static Date getBeginDateByTimezone(Date date) {
		Calendar cal = new GregorianCalendar();
		cal.setTime(date);
		getBeginDate(cal);

		date = cal.getTime();

		Double timeZoneHourOffset = findTimeZoneHourOffset(date);
		DateTime dt = new DateTime(date.getTime());
		dt = dt.plusMinutes((int) (timeZoneHourOffset * 60));

		return dt.toDate();
	}

	public static Date getEndDate(Date date) {
		Calendar cal = new GregorianCalendar();
		cal.setTime(date);
		getEndDate(cal);
		return cal.getTime();
	}

	public static Date max(Date date1, Date date2) {
		if (date1 != null && date2 != null) {
			return date2.after(date1) ? date2 : date1;
		}

		if (date1 != null) {
			return date1;
		}

		return date2;
	}

	public static Date min(Date date1, Date date2) {
		if (date1 != null && date2 != null) {
			return date2.after(date1) ? date1 : date2;
		}

		if (date1 != null) {
			return date1;
		}

		return date2;
	}

	public static Date getEndDateByTimezone(Date date) {
		Calendar cal = new GregorianCalendar();
		cal.setTime(date);
		getEndDate(cal);

		date = cal.getTime();

		Double timeZoneHourOffset = findTimeZoneHourOffset(date);

		DateTime dt = new DateTime(date.getTime());
		dt = dt.plusMinutes((int) (timeZoneHourOffset * 60));

		return dt.toDate();
	}

	public static Date getBeginWorkWeekDate(Date weekDate) {
		Date retDate = null;
		if (weekDate != null) {
			Calendar theDate = Calendar.getInstance();
			theDate.setTime(weekDate);
			Calendar calc = new GregorianCalendar(theDate.get(Calendar.YEAR), theDate.get(Calendar.MONTH), theDate.getActualMaximum(Calendar.DAY_OF_MONTH));

			int dayOfWeek = calc.get(Calendar.DAY_OF_WEEK);
			int beginWeekVal = Calendar.MONDAY - dayOfWeek;
			calc.add(Calendar.DAY_OF_MONTH, beginWeekVal);
			retDate = calc.getTime();
		}
		return retDate;
	}

	public static Date getEndWorkWeekDate(Date weekDate) {
		Date retDate = null;
		if (weekDate != null) {
			Calendar theDate = Calendar.getInstance();
			theDate.setTime(weekDate);
			Calendar calc = new GregorianCalendar(theDate.get(Calendar.YEAR), theDate.get(Calendar.MONTH), theDate.get(Calendar.DATE));

			int dayOfWeek = calc.get(Calendar.DAY_OF_WEEK);
			int endWeekVal = Calendar.FRIDAY - dayOfWeek;
			calc.add(Calendar.DAY_OF_MONTH, endWeekVal);
			retDate = calc.getTime();
		}
		return retDate;
	}

	public static Date getFirstDateofAWeek(Date date) {
		Date monday = null;
		Calendar rightNow = Calendar.getInstance();
		rightNow.setTime(date);
		int day = rightNow.get(Calendar.DAY_OF_WEEK);
		int distance = 0;

		if (day == Calendar.MONDAY)
			monday = rightNow.getTime();
		else {
			distance = day - Calendar.MONDAY;
			if (distance == -1)
				distance = 6;

			monday = (Date) (rightNow.getTime());
			monday.setTime(monday.getTime() - 1000 * 60 * 60 * 24 * (distance));
		}
		return getBeginDate(monday);
	}

	public static int getDayOfWeek(Date date) {
		Calendar dateCal = Calendar.getInstance();
		dateCal.setTime(date);
		dateCal = new GregorianCalendar(dateCal.get(Calendar.YEAR), dateCal.get(Calendar.MONTH), dateCal.get(Calendar.DATE));
		return dateCal.get(Calendar.DAY_OF_WEEK);
	}

	public static Date nextNDay(Date date, int day) {
		Date retDate = null;
		if (date != null) {
			Calendar theDate = new GregorianCalendar();
			theDate.setTimeZone(TimeZone.getTimeZone("UTC"));
			theDate.setTime(date);
			theDate.add(Calendar.DATE, day);
			retDate = theDate.getTime();
		}
		return retDate;
	}

	public static long getDayCount(Date start, Date end) {
		return (end.getTime() - start.getTime()) / ONE_DAY;
	}

	public static Date today() {
		Calendar now = Calendar.getInstance();
		Calendar today = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DATE));
		return today.getTime();
	}

	public static Date tomorrow(Date date) {
		return nextNDay(date, 1);
	}

	public static Date yesterday(Date date) {
		return nextNDay(date, -1);
	}

	public static boolean isSameDay(Date date1, Date date2) {
		if (date1 != null && date2 != null) {
			Calendar date1Cal = Calendar.getInstance();
			date1Cal.setTime(date1);
			date1Cal = new GregorianCalendar(date1Cal.get(Calendar.YEAR), date1Cal.get(Calendar.MONTH), date1Cal.get(Calendar.DATE));
			Calendar date2Cal = Calendar.getInstance();
			date2Cal.setTime(date2);
			date2Cal = new GregorianCalendar(date2Cal.get(Calendar.YEAR), date2Cal.get(Calendar.MONTH), date2Cal.get(Calendar.DATE));
			return date1Cal.equals(date2Cal);
		}
		return false;
	}

	public static Date formatDate(String dateFormat, String dateStr) {
		Date date = new Date();

		java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(dateFormat);
		try {
			date = sdf.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}

		return date;
	}

	public static Date formatDate(String dateFormat, Date date) {
		Date formattedDate = null;

		SimpleDateFormat formatter;
		try {
			formatter = new SimpleDateFormat(dateFormat);
			String dateStr = formatter.format(date);
			formattedDate = formatter.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}

		return formattedDate;
	}

	public static Date createDate(int year, int month, int day, int hour, int minute, int second, int milliSecond) {
		Calendar cal = new GregorianCalendar();
		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.MONTH, month - 1);
		cal.set(Calendar.DAY_OF_MONTH, day);
		cal.set(Calendar.HOUR_OF_DAY, hour);
		cal.set(Calendar.MINUTE, minute);
		cal.set(Calendar.SECOND, second);
		cal.set(Calendar.MILLISECOND, milliSecond);
		return cal.getTime();
	}

	public static String formatDateWithTimezone(String dateFormat, Date date, Double offset) {
		SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
		String offsetStr = getGMTStringFromOffset(offset);
		TimeZone timeZone = TimeZone.getTimeZone(offsetStr);
		sdf.setTimeZone(timeZone);
		return sdf.format(date.getTime());
	}

	public static Double findTimeZoneHourOffset(Date date) {
		if (date != null) {
			DateTime dt = new DateTime(date.getTime());
			if (dt != null) {
				DateTimeZone tz = dt.getZone();
				if (tz != null) {
					int offset = tz.getOffset(date.getTime());
					return offset / (double) (60 * 60 * 1000);
				}
			}
		}

		return 0D;
	}

	public static String convertDateToStr(Date date) {
		return convertDateToStr(date.getTime());
	}

	public static String convertDateToStr(long timestamp) {
		StringBuffer sbf = new StringBuffer();

		DateTime dt = new DateTime(timestamp);

		int year = dt.getYear();
		sbf.append(year).append("-");

		int month = dt.getMonthOfYear();
		if (month < 10) {
			sbf.append("0");
		}
		sbf.append(month).append("-");

		int dayOfMonth = dt.getDayOfMonth();
		if (dayOfMonth < 10) {
			sbf.append("0");
		}
		sbf.append(dayOfMonth);

		return sbf.toString();
	}

	public static String getGMTStringFromOffset(double offset) {
		int hour = (int) offset;
		int minute = Math.abs((int) (offset * 60 - hour * 60));
		String offsetStr;
		if (offset < 0) {
			offsetStr = "GMT" + hour + ":" + (minute < 10 ? minute + "0" : minute);
		} else {
			offsetStr = "GMT+" + hour + ":" + (minute < 10 ? minute + "0" : minute);
		}
		return offsetStr;
	}

	public static int getHourOfDay(Date date) {
		DateTime dt = new DateTime(date);
		return dt.getHourOfDay();
	}

	public static int getHourOfDay(long date) {
		DateTime dt = new DateTime(new Date(date));
		return dt.getHourOfDay();
	}

	private static void getBeginDate(Calendar cal) {
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
	}

	private static void getBeginHour(Calendar cal) {
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
	}

	private static void getEndDate(Calendar cal) {
		cal.set(Calendar.HOUR_OF_DAY, 23);
		cal.set(Calendar.MINUTE, 59);
		cal.set(Calendar.SECOND, 59);
		cal.set(Calendar.MILLISECOND, 999);
	}
}
